package testing;
import static org.junit.Assert.*;

import org.junit.Test;

import visitor.FindVisitor;
import visitor.Parameters;
import visitor.SendVisitor;
import visitor.TrackSendPathVisitor;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;

import hypeerweb.HyPeerWeb;
import hypeerweb.Node;
import hypeerweb.SimplifiedNodeDomain;
import hypeerweb.WebId;

/*
 * Create JUnit test cases for some class of your choosing. 
 * Perform 4 forms of whitebox testing: loop testing, relational 
 * testing, internal boundary value testing, and dataflow testing.
 *  Also, you are to provide a code coverage report showing 100% (or as close as possible) 
 *  code coverage on the methods tested. You only need test enough methods 
 *  to demonstrate the four forms of whitebox testing. Your JUnit test cases
 *   should be well documented for it is the documentation I will grade.

Grading Criteria
 */

public class CarlTests {
	public static HyPeerWeb hypeerweb = null;

	//Loop Testing
	@Test
	public void test() {
		hypeerweb = HyPeerWeb.getSingleton();

		assertTrue(loopTest());
		assertTrue(relationalTesting());
		assertTrue(internalBoundary());
		assertTrue(dataflowTesting());
		
		
	}
	private boolean loopTest(){
		//This tests the loops through the send classes visit method
		
		int target = 0;
		createHyPeerWebWith(32);
		TrackSendPathVisitor trackSendPathVisitor = new TrackSendPathVisitor();
		Parameters parameters = new Parameters();
		parameters.set(SendVisitor.TARGET_KEY, target);
		
		ArrayList<Integer> emptyList = new ArrayList<Integer>();
		parameters.set(TrackSendPathVisitor.PATH_KEY, emptyList);
		hypeerweb.getNode(0).accept(trackSendPathVisitor, parameters);
		ArrayList<ArrayList<Integer>> finalResult = (ArrayList<ArrayList<Integer>>) hypeerweb.getNode(target).getContents().get(TrackSendPathVisitor.PATHS_KEY);

		if(finalResult.get(0).size() != 1)
			return false;
		

		target=16;
		parameters.set(SendVisitor.TARGET_KEY,target);
		emptyList = new ArrayList<Integer>();
		parameters.set(TrackSendPathVisitor.PATH_KEY, emptyList);
		hypeerweb.getNode(0).accept(trackSendPathVisitor, parameters);
		finalResult = (ArrayList<ArrayList<Integer>>) hypeerweb.getNode(target).getContents().get(TrackSendPathVisitor.PATHS_KEY);

		if(finalResult.get(0).size() != 2)
			return false;
		
		target=31;
		parameters.set(SendVisitor.TARGET_KEY,target);
		emptyList = new ArrayList<Integer>();
		parameters.set(TrackSendPathVisitor.PATH_KEY, emptyList);
		hypeerweb.getNode(0).accept(trackSendPathVisitor, parameters);
		finalResult = (ArrayList<ArrayList<Integer>>) hypeerweb.getNode(target).getContents().get(TrackSendPathVisitor.PATHS_KEY);

		if(finalResult.get(0).size() != 2)
			return false;
		

		return true;
	}
	private boolean relationalTesting(){
		//Tests the validity of the finding and checking of the send visitor class. 
		
		createHyPeerWebWith(64);
		Node node0 = hypeerweb.getNode(0);
		FindVisitor fVisit=new FindVisitor();
		
		//two past the end
		Parameters params=fVisit.createInitialParameters(new WebId(0));
		//start the chain of sends
		node0.accept(fVisit, params);
		if((Node) fVisit.getParameters().get(fVisit.FINAL_NODE) != node0)
			return false;
		
		params=fVisit.createInitialParameters(new WebId(17));
		//start the chain of sends
		node0.accept(fVisit, params);
		if((Node) fVisit.getParameters().get(fVisit.FINAL_NODE) != hypeerweb.getNode(17))
			return false;
	
		
		return true;
	}
	
	
	
	
	private boolean internalBoundary(){
		//Testing the boundaries of the hypeerweb
		
		//////////////////////////////////////////
		/* Testing Past the end of the hypeerweb*/
		createHyPeerWebWith(10);
		Node node0 = hypeerweb.getNode(0);
		FindVisitor fVisit=new FindVisitor();
		
		//two past the end
		Parameters params=fVisit.createInitialParameters(new WebId(11));
		//start the chain of sends
		node0.accept(fVisit, params);
		if( (Node) fVisit.getParameters().get(fVisit.FINAL_NODE) != null)
			return false;
		
		//one past
		params=fVisit.createInitialParameters(new WebId(10));
		node0.accept(fVisit, params);
		if( (Node) fVisit.getParameters().get(fVisit.FINAL_NODE) != null)
			return false;
		
		//on boundary
		params=fVisit.createInitialParameters(new WebId(9));
		node0.accept(fVisit, params);
		if( (Node) fVisit.getParameters().get(fVisit.FINAL_NODE) == null)
			return false;
		//one before
		params=fVisit.createInitialParameters(new WebId(8));
		node0.accept(fVisit, params);
		if( (Node) fVisit.getParameters().get(fVisit.FINAL_NODE) == null)
			return false;

		
		//Run on a much larger
		createHyPeerWebWith(32);
		node0 = hypeerweb.getNode(0);
		
		//two past the end
		params=fVisit.createInitialParameters(new WebId(33));
		//start the chain of sends
		node0.accept(fVisit, params);
		if( (Node) fVisit.getParameters().get(fVisit.FINAL_NODE) != null)
			return false;
		
		//one past
		params=fVisit.createInitialParameters(new WebId(32));
		node0.accept(fVisit, params);
		if( (Node) fVisit.getParameters().get(fVisit.FINAL_NODE) != null)
			return false;
		
		//on boundary
		params=fVisit.createInitialParameters(new WebId(31));
		node0.accept(fVisit, params);
		if( (Node) fVisit.getParameters().get(fVisit.FINAL_NODE) == null)
			return false;
		//one before
		params=fVisit.createInitialParameters(new WebId(30));
		node0.accept(fVisit, params);
		if( (Node) fVisit.getParameters().get(fVisit.FINAL_NODE) == null)
			return false;

		//////////////////////////////////////////
		/* Testing before the beginning of the hypeerweb*/
		createHyPeerWebWith(10);
		node0 = hypeerweb.getNode(0);
		
		//two before
		params=fVisit.createInitialParameters(new WebId(-2));
		//start the chain of sends
		node0.accept(fVisit, params);
		if( (Node) fVisit.getParameters().get(fVisit.FINAL_NODE) != null)
			return false;
		
		//one before
		params=fVisit.createInitialParameters(new WebId(-1));
		node0.accept(fVisit, params);
		if( (Node) fVisit.getParameters().get(fVisit.FINAL_NODE) != null)
			return false;
		
		//on boundary
		params=fVisit.createInitialParameters(new WebId(0));
		node0.accept(fVisit, params);
		if( (Node) fVisit.getParameters().get(fVisit.FINAL_NODE) == null)
			return false;
		//one after
		params=fVisit.createInitialParameters(new WebId(1));
		node0.accept(fVisit, params);
		if( (Node) fVisit.getParameters().get(fVisit.FINAL_NODE) == null)
			return false;
		createHyPeerWebWith(32);
		node0 = hypeerweb.getNode(0);
		
		//two before
		params=fVisit.createInitialParameters(new WebId(-2));
		//start the chain of sends
		node0.accept(fVisit, params);
		if( (Node) fVisit.getParameters().get(fVisit.FINAL_NODE) != null)
			return false;
		
		//one before
		params=fVisit.createInitialParameters(new WebId(-1));
		node0.accept(fVisit, params);
		if( (Node) fVisit.getParameters().get(fVisit.FINAL_NODE) != null)
			return false;
		
		//on boundary
		params=fVisit.createInitialParameters(new WebId(0));
		node0.accept(fVisit, params);
		if( (Node) fVisit.getParameters().get(fVisit.FINAL_NODE) == null)
			return false;
		//one after
		params=fVisit.createInitialParameters(new WebId(1));
		node0.accept(fVisit, params);
		if( (Node) fVisit.getParameters().get(fVisit.FINAL_NODE) == null)
			return false;
		return true;
	}
	private boolean dataflowTesting(){
		//tests the flow of the visitor through the hypeerweb.
		for (int i = 0; i < sizes.length; i++) {
			int hypeerwebSize = sizes[i];
			int target = targets[i];
			boolean testSendError = testSend(hypeerwebSize, target);
			if (testSendError) {
				System.out.println("Error in DataFlow: i="+i);
				return false;
			}
		}
		return true;
	}
	
	
	static private void createHyPeerWebWith(int numberOfNodes) {
		hypeerweb.clear();
		Node node0 = new Node(0);
		hypeerweb.addToHyPeerWeb(node0, null);

		for (int i = 1; i < numberOfNodes; i++) {
			Node node = new Node(0);
			node0.addToHyPeerWeb(node);
		}
	}
	private static boolean testSend(int hypeerwebSize, int target) {
		createHyPeerWebWith(hypeerwebSize);

		for (int i = 0; i < hypeerwebSize; i++) {
			TrackSendPathVisitor trackSendPathVisitor = new TrackSendPathVisitor();
			Parameters parameters = new Parameters();
			parameters.set(SendVisitor.TARGET_KEY, target);
			ArrayList<Integer> emptyList = new ArrayList<Integer>();
			parameters.set(TrackSendPathVisitor.PATH_KEY, emptyList);
			hypeerweb.getNode(i).accept(trackSendPathVisitor, parameters);
		}
		ArrayList<ArrayList<Integer>> finalResult = (ArrayList<ArrayList<Integer>>) hypeerweb
				.getNode(target).getContents()
				.get(TrackSendPathVisitor.PATHS_KEY);
		return checkFinalResult1(finalResult, target);
	}

	private static boolean checkFinalResult1(ArrayList<ArrayList<Integer>> paths, int target) {
		boolean result = false;
		for (ArrayList<Integer> path : paths) {
			int loc = checkFinalResult(path, target);
			if (loc != -1)
				return true;
				
		}
		return result;
	}

	private static int checkFinalResult(ArrayList<Integer> path, int targetId) {
		int result = -1;
		for (int i = 0; result == -1 && i < path.size() - 1; i++) {
			if (!isBestChoice(path.get(i), path.get(i + 1), targetId)) {
				result = i;
			}
		}
		return result;
	}

	private static boolean isBestChoice(int source, int nextNode, int targetId) {
		SimplifiedNodeDomain sourceNode = hypeerweb.getNode(source)
				.constructSimplifiedNodeDomain();
		boolean result = !sourceNode.containsCloserNode(nextNode, targetId);
		return result;
	}

	private static int[] sizes = { 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 10, 53, 39,
			46, 67, 75, 25, 85, 43, 86, 61, 127, 75, 84, 34, 21, 113, 7, 77,
			65, 70, 41, 69, 114, 128, 91, 96, 38, 31, 48, 21, 113, 101, 100,
			42, 21, 47, 71, 100, 114 };


	private static int[] targets = { 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 4, 14, 15,
			1, 60, 50, 5, 51, 35, 71, 41, 121, 58, 5, 1, 0, 108, 1, 74, 60, 4,
			20, 44, 63, 54, 70, 27, 27, 4, 14, 9, 46, 66, 12, 17, 13, 18, 39,
			44, 107 };

}

